"""
弹性图片处理器
高可靠性版本，具备错误恢复和重试机制
"""

import time
import json
from pathlib import Path
from typing import Dict, Any, Optional, List
from .ollama_client_stdlib import OllamaClient


class ResilientImageProcessor:
    """弹性图片处理器"""
    
    def __init__(self, ollama_config: Dict[str, Any], performance_config: Dict[str, Any]):
        """
        初始化弹性图片处理器
        
        Args:
            ollama_config: Ollama配置
            performance_config: 性能配置
        """
        self.ollama_config = ollama_config
        self.performance_config = performance_config
        
        # 创建Ollama客户端
        host = f"http://{ollama_config.get('host', 'localhost')}:{ollama_config.get('port', 11434)}"
        model = ollama_config.get('model', 'qwen2.5-vl:7b')
        system_prompt = ollama_config.get('system_prompt')
        self.client = OllamaClient(host, model, system_prompt)
        
        # 弹性配置
        self.max_retries = ollama_config.get('max_retries', 5)  # 增加重试次数
        self.delay = performance_config.get('delay', 3.0)  # 增加延迟
        self.backoff_factor = performance_config.get('backoff_factor', 2.0)
        self.retry_delay = performance_config.get('retry_delay', 5.0)
        
        # 错误恢复
        self.error_log_file = "error_log.json"
        self.failed_images = []
        self.recovery_attempts = {}
        
        # 健康检查
        self.health_check_interval = performance_config.get('health_check_interval', 10)
        self.last_health_check = 0
        
        # 统计信息
        self.stats = {
            'total_processed': 0,
            'total_failed': 0,
            'total_retries': 0,
            'total_time': 0,
            'avg_time': 0,
            'recoveries': 0
        }
    
    def _health_check(self) -> bool:
        """执行健康检查"""
        current_time = time.time()
        if current_time - self.last_health_check < self.health_check_interval:
            return True
        
        try:
            # 检查Ollama连接
            if not self.client.check_connection():
                print("健康检查失败: Ollama服务不可用")
                return False
            
            # 检查模型可用性
            if not self.client.check_model_available():
                print(f"健康检查失败: 模型 {self.client.model} 不可用")
                return False
            
            self.last_health_check = current_time
            return True
            
        except Exception as e:
            print(f"健康检查异常: {e}")
            return False
    
    def _log_error(self, image_path: str, error: str, attempt: int):
        """记录错误"""
        error_entry = {
            'timestamp': time.time(),
            'image_path': image_path,
            'error': error,
            'attempt': attempt
        }
        
        self.failed_images.append(error_entry)
        
        # 写入错误日志文件
        try:
            with open(self.error_log_file, 'a', encoding='utf-8') as f:
                f.write(json.dumps(error_entry, ensure_ascii=False) + '\n')
        except Exception as e:
            print(f"写入错误日志失败: {e}")
    
    def _calculate_retry_delay(self, attempt: int) -> float:
        """计算重试延迟（指数退避）"""
        return min(self.retry_delay * (self.backoff_factor ** (attempt - 1)), 60.0)
    
    def _attempt_recovery(self, image_path: str) -> bool:
        """尝试恢复失败的图片"""
        if image_path not in self.recovery_attempts:
            self.recovery_attempts[image_path] = 0
        
        self.recovery_attempts[image_path] += 1
        
        if self.recovery_attempts[image_path] > 3:
            print(f"恢复尝试次数已达上限: {image_path}")
            return False
        
        print(f"尝试恢复失败图片: {image_path} (第{self.recovery_attempts[image_path]}次)")
        
        # 等待一段时间后重试
        time.sleep(self._calculate_retry_delay(self.recovery_attempts[image_path]))
        
        try:
            description = self.generate_description(image_path)
            if description:
                self.stats['recoveries'] += 1
                print(f"恢复成功: {image_path}")
                return True
        except Exception as e:
            print(f"恢复失败: {image_path} - {e}")
        
        return False
    
    def generate_description(self, image_path: str) -> Optional[str]:
        """
        生成图片描述（弹性模式）
        
        Args:
            image_path: 图片文件路径
            
        Returns:
            图片描述文本
        """
        start_time = time.time()
        
        try:
            image_path = Path(image_path)
            
            # 检查文件是否存在
            if not image_path.exists():
                raise FileNotFoundError(f"图片文件不存在: {image_path}")
            
            # 健康检查
            if not self._health_check():
                print("健康检查失败，等待服务恢复...")
                time.sleep(self.retry_delay)
            
            # 多次重试
            for attempt in range(1, self.max_retries + 1):
                try:
                    print(f"处理图片: {image_path.name} (尝试 {attempt}/{self.max_retries})")
                    
                    # 分析图片
                    description = self.client.analyze_image(image_path, 1)  # 单次重试
                    
                    if description:
                        self.stats['total_processed'] += 1
                        print(f"处理成功: {image_path.name}")
                        return description
                    else:
                        print(f"分析失败: {image_path.name}")
                        
                except Exception as e:
                    print(f"处理异常: {image_path.name} - {e}")
                    self._log_error(str(image_path), str(e), attempt)
                    self.stats['total_retries'] += 1
                
                # 如果不是最后一次尝试，等待后重试
                if attempt < self.max_retries:
                    delay = self._calculate_retry_delay(attempt)
                    print(f"等待 {delay:.1f} 秒后重试...")
                    time.sleep(delay)
            
            # 所有尝试都失败
            self.stats['total_failed'] += 1
            print(f"处理失败，已达到最大重试次数: {image_path.name}")
            return None
            
        except Exception as e:
            self.stats['total_failed'] += 1
            self._log_error(str(image_path), str(e), 0)
            print(f"弹性处理图片失败: {image_path} - {e}")
            return None
        
        finally:
            # 更新统计
            processing_time = time.time() - start_time
            self.stats['total_time'] += processing_time
            total_operations = self.stats['total_processed'] + self.stats['total_failed']
            if total_operations > 0:
                self.stats['avg_time'] = self.stats['total_time'] / total_operations
    
    def recover_failed_images(self, failed_image_paths: List[str]) -> Dict[str, Any]:
        """
        尝试恢复失败的图片
        
        Args:
            failed_image_paths: 失败的图片路径列表
            
        Returns:
            恢复结果统计
        """
        print(f"开始恢复 {len(failed_image_paths)} 张失败的图片...")
        
        recovery_stats = {
            'total': len(failed_image_paths),
            'recovered': 0,
            'still_failed': 0
        }
        
        for image_path in failed_image_paths:
            if self._attempt_recovery(image_path):
                recovery_stats['recovered'] += 1
            else:
                recovery_stats['still_failed'] += 1
        
        print(f"恢复完成: {recovery_stats['recovered']}/{recovery_stats['total']} 张图片成功")
        return recovery_stats
    
    def get_statistics(self) -> Dict[str, Any]:
        """获取统计信息"""
        stats = self.stats.copy()
        stats['failed_images_count'] = len(self.failed_images)
        stats['recovery_attempts_count'] = len(self.recovery_attempts)
        
        # 计算成功率
        total_operations = stats['total_processed'] + stats['total_failed']
        if total_operations > 0:
            stats['success_rate'] = stats['total_processed'] / total_operations
        else:
            stats['success_rate'] = 0
        
        return stats
    
    def reset_statistics(self) -> None:
        """重置统计信息"""
        self.stats = {
            'total_processed': 0,
            'total_failed': 0,
            'total_retries': 0,
            'total_time': 0,
            'avg_time': 0,
            'recoveries': 0
        }
        self.failed_images.clear()
        self.recovery_attempts.clear()
        
        # 清空错误日志文件
        try:
            if Path(self.error_log_file).exists():
                Path(self.error_log_file).unlink()
        except Exception as e:
            print(f"清空错误日志失败: {e}")
    
    def export_error_log(self, output_file: str = None) -> str:
        """
        导出错误日志
        
        Args:
            output_file: 输出文件路径
            
        Returns:
            导出文件路径
        """
        if output_file is None:
            output_file = f"error_log_export_{int(time.time())}.json"
        
        try:
            with open(output_file, 'w', encoding='utf-8') as f:
                json.dump(self.failed_images, f, indent=2, ensure_ascii=False)
            
            print(f"错误日志已导出到: {output_file}")
            return output_file
            
        except Exception as e:
            print(f"导出错误日志失败: {e}")
            return ""